#include "interrupt.h"

#define MAKE_IDT_DESCRIPTOR(lpIdtDesc, lpfnHandler, wSelector, bAttribute)     \
  lpIdtDesc->OffsetLow = (WORD)(DWORD_PTR)lpfnHandler;                         \
  lpIdtDesc->Selector = wSelector;                                             \
  lpIdtDesc->Count = 0;                                                        \
  lpIdtDesc->Attribute = bAttribute;                                           \
  lpIdtDesc->OffsetHigh = (WORD)((DWORD_PTR)lpfnHandler >> 16)

SyscallProc SyscallTable[SYSCALL_CNT];
extern uint32_t SyscallHandler(void);

STATIC struct IDT_DESC IDT[IDT_DESC_CNT];

IntrHandler IDTTable[INTR_ENTRY_CNT];
extern IntrHandler IntrEntryTable[INTR_ENTRY_CNT];

STATIC void IdtGeneralHandler(BYTE bVector) {
  register LPBYTE lpByte = (LPBYTE)(DWORD_PTR)(0xc00b8001 + (DWORD)bVector * 2);
  *lpByte = ~*lpByte;
  register LPCPU_CONTEXT lpCpu;
  static CHAR lpszMsg[0x100];
  register DWORD i;
  __asm__("lea 12(%%ebp),%0" : "=r"(lpCpu));
  RtlSPrintf(lpszMsg, "cs: %x  eip: %x  vec: %x  err: %x", lpCpu->cs,
             lpCpu->eip, bVector, lpCpu->err);
  for (i = 0; lpszMsg[i]; ++i) {
    *(CHAR *)(DWORD_PTR)(0xc00b80a0 + i * 2) = lpszMsg[i];
  }
}

STATIC void IdtTimerHandler(void) {
  //BYTE bVector参数还在栈上，只是我不要了而已，C语言式可变参数...
  register LPCPU_CONTEXT lpCpu;
  //__asm__("lea 12(%%ebp),%0" : "=r"(lpCpu));
  lpCpu = &((LPTHREAD_CONTEXT)ThHandles[0])->cpu;
  //RtlCopyMemory(lpCpu, &((LPTHREAD_CONTEXT)ThHandles[0])->cpu, sizeof(struct CPU_CONTEXT));
  static CHAR lpszMsg[0x100];
  register DWORD i;
  RtlSPrintf(lpszMsg, "cs: %x  eip: %x  err: %x", lpCpu->cs,
             lpCpu->eip, lpCpu->err);
  for (i = 0; lpszMsg[i]; ++i) {
    *(CHAR *)(DWORD_PTR)(0xc00b8140 + i * 2) = lpszMsg[i];
  }
}

STATIC void IdtKeyboardHandler(void) {
  static CHAR pKey[4];
  register BYTE bKey;
  READ_PORT_BYTE(0x60, bKey);
  RtlSPrintf(pKey, "?%x", bKey);
  __SYSCALL1(3, pKey);
}

void IdtInitialize(void) {
  register LPIDT_DESC lpIdtDesc;
  register uint32_t i;
  for (i = 0; i < INTR_ENTRY_CNT; ++i) {
    lpIdtDesc = IDT + i;
    MAKE_IDT_DESCRIPTOR(lpIdtDesc, IntrEntryTable[i], 0x0008,
                        INTR_P | INTR_DPL_0 | INTR_TYPE_INT32);
    IDTTable[i] = IdtGeneralHandler;
  }
  IDTTable[0x20] = IdtTimerHandler;
  IDTTable[0x21] = IdtKeyboardHandler;
  lpIdtDesc = IDT + SYSCALL_INTR;
  MAKE_IDT_DESCRIPTOR(lpIdtDesc, SyscallHandler, 0x0008,
                      INTR_P | INTR_DPL_3 | INTR_TYPE_TRAP32);
  struct {
    WORD wUnused;
    WORD wLimit;
    DWORD dwBase;
  } IDTPtr;
  IDTPtr.wLimit = (IDT_DESC_CNT << 3) - 1;
  IDTPtr.dwBase = (DWORD)(DWORD_PTR)IDT;
  __LIDT(&IDTPtr.wLimit);
}
